home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 8614 / 8614.xpi / modules / storage / LoggingDB.jsm
Text File  |  2010-02-10  |  29KB  |  993 lines

  1. // DO NOT import this into the global namespace, but instead
  2. // import it into your own namespace wrapper
  3.  
  4. var EXPORTED_SYMBOLS = ["LOGGING_DB"];
  5.  
  6. Components.utils.import("resource://glydo/utils/prototype_xul_1_6_0_3_modified.jsm");
  7. Components.utils.import("resource://glydo/utils/io.jsm");
  8. Components.utils.import("resource://glydo/utils/Prefs.jsm");
  9. Components.utils.import("resource://glydo/utils/Utils.jsm");
  10. Components.utils.import("resource://glydo/utils/PrivateBrowsing.jsm");
  11. Components.utils.import("resource://glydo/ClientInfo.jsm");
  12.  
  13. LoggingDB = Prototype.Class.create({
  14.     initialize: function() {
  15.         this.setupSchemaVersionsInfo();
  16.         this.available = false;
  17.         this.privateBrowsing = new PrivateBrowsingListener();
  18.         this.privateBrowsing.addListener(this);
  19.         this.setupDatabase();
  20.     },
  21.  
  22.     setupDatabase: function() {
  23.         this.schemaVersion = null;
  24.         this.creatorClient = null;
  25.         this.instanceId = null;
  26.         this.storageService = Components.classes["@mozilla.org/storage/service;1"]
  27.                                  .getService(Components.interfaces.mozIStorageService);
  28.         var file = DirIO.get("ProfD");
  29.         file.append("glydo");
  30.         file.append("storage");
  31.         DirIO.create(file);
  32.         this.usingPrivateDatabase = this.privateBrowsing.inPrivateBrowsing;
  33.         file.append(this.usingPrivateDatabase ? "private.logging.sqlite" : "logging.sqlite");
  34.         this.databaseFile = file;
  35.       this.updateSchema();
  36.     },
  37.     
  38.     cleanPrivateDatabase: function() {
  39.         if (this.usingPrivateDatabase && this.databaseFile) {
  40.             this.databaseFile.remove(false);
  41.             this.databaseFile = null;
  42.             this.schemaVersion = null;
  43.             this.creatorClient = null;
  44.             this.instanceId = null;
  45.         }
  46.     },
  47.     
  48.     onEnterPrivateBrowsing: function() {
  49.         // We setup the database again, setting up the private database
  50.         this.setupDatabase();
  51.     },
  52.     
  53.     onExitPrivateBrowsing: function() {
  54.         // We clean the private database
  55.         this.cleanPrivateDatabase();
  56.         // We setup the new database
  57.         this.setupDatabase();
  58.     },
  59.         
  60.     
  61.     setupSchemaVersionsInfo: function() {
  62.         this.schemaVersionsInfo = [
  63.         {
  64.             version: "0.1",
  65.             upgrade: this.upgradeToV0_1
  66.         },
  67.         {
  68.             version: "0.2",
  69.             upgrade: this.upgradeToV0_2
  70.         },
  71.         {
  72.             version: "0.3",
  73.             upgrade: this.upgradeToV0_3
  74.         },
  75.         {
  76.             version: "0.4",
  77.             upgrade: this.upgradeToV0_4
  78.         }
  79.     ];
  80.     },
  81.     
  82.     connect: function(force) {
  83.         if (!force && !this.available) {
  84.             return null;
  85.         }
  86.       var conn = this.storageService.openDatabase(this.databaseFile);
  87.       if (!conn.connectionReady) {
  88.           return null;
  89.       }
  90.       return conn;
  91.     },
  92.     
  93.     updateSchema: function() {
  94.         var conn = null;
  95.         var ourTransaction = false;
  96.         try {
  97.             conn = this.connect(true);
  98.             if (!conn) {
  99.                 return;
  100.             }
  101.             conn.beginTransaction();
  102.             var baseSchema = true;
  103.             ourTransaction = true;
  104.             this.getSchemaInfo(conn);
  105.             
  106.             // We first find the next schema version in the schema versions table
  107.             var nSchemaVersions = this.schemaVersionsInfo.length;
  108.             var nextSchemaVersionIndex = 0;
  109.             if (this.schemaVersion !== null) {
  110.                 baseSchema = false;
  111.                 for (var i = 0; i < nSchemaVersions; ++i) {
  112.                     var schemaVersionInfo = this.schemaVersionsInfo[i];
  113.                     if (schemaVersionInfo.version == this.schemaVersion) {
  114.                         nextSchemaVersionIndex = i+1;
  115.                         baseSchema = true;
  116.                         break;
  117.                     }
  118.                 }
  119.             }
  120.             
  121.             // If the current schema version is unrecognized we abort,
  122.             // leaving the DB in unavailable state
  123.             if (!baseSchema) {
  124.                 return;
  125.             }
  126.  
  127.             // Otherwise we upgrade the schema by steps
  128.             for (var j = nextSchemaVersionIndex; j < nSchemaVersions; ++j) {
  129.                 var schemaVersionInfo = this.schemaVersionsInfo[j];
  130.                 this.upgradeToSchemaVersion(conn,schemaVersionInfo);
  131.             }
  132.             // Update info from schema again
  133.             this.getSchemaInfo(conn);
  134.  
  135.             conn.commitTransaction();
  136.             //FIXME: This won't really work well in high concurrency..
  137.             ourTransaction = false;
  138.             this.available = true;
  139.         } catch (e) {
  140.             
  141.             if (window.Components) {
  142.                 Components.utils.reportError(e);
  143.             }
  144.         } finally { 
  145.             if (conn) {
  146.                 if (ourTransaction && conn.transactionInProgress) {
  147.                     
  148.                     conn.rollbackTransaction();
  149.                     
  150.                 }
  151.                 try {
  152.                     conn.close();
  153.                 } catch (ex) {
  154.                 }
  155.                 conn = null;
  156.             }
  157.         }
  158.     },
  159.     
  160.     upgradeToSchemaVersion: function(conn,schemaVersionInfo) {
  161.         var creatorClient = CLIENT_INFO.clientId + ":" + CLIENT_INFO.version;
  162.         var instanceId = this.instanceId;
  163.         if (instanceId === null) {
  164.             instanceId = Utils.uuid1();
  165.         }
  166.         
  167.         schemaVersionInfo.upgrade.call(this,conn,schemaVersionInfo.version);
  168.         
  169.         var stmt = null;
  170.         try {
  171.             stmt = conn.createStatement("INSERT INTO schema_version (version,created_by_client,instance_id) VALUES (?1,?2,?3)");
  172.             stmt.bindUTF8StringParameter(0,schemaVersionInfo.version);
  173.             stmt.bindUTF8StringParameter(1,creatorClient);
  174.             stmt.bindUTF8StringParameter(2,instanceId);
  175.             stmt.execute();
  176.         } finally {
  177.             if (stmt) {
  178.                 try {
  179.                     try {
  180.                         stmt.finalize();
  181.                     } catch (ex) {
  182.                     }
  183.                 } catch (ex) {
  184.                 }
  185.                 stmt = null;
  186.             }
  187.         }
  188.     },
  189.     
  190.     createBaseReportRow: function(conn,time) {
  191.         var stmt = null;
  192.         try {
  193.             stmt = conn.createStatement("REPLACE INTO last_report (id,report_time) VALUES (1,?1)");
  194.             stmt.bindUTF8StringParameter(0,time.getTime());
  195.             stmt.execute();
  196.         } finally {
  197.             if (stmt) {
  198.                 try {
  199.                     stmt.finalize();
  200.                 } catch (ex) {
  201.                 }
  202.                 stmt = null;
  203.             }
  204.         }
  205.     },
  206.     
  207.     getSchemaInfo: function(conn) {
  208.         this.schemaVersion = null;
  209.         this.creatorClient = null;
  210.         this.instanceId = null;
  211.         var stmt = null;
  212.         try {
  213.             stmt = conn.createStatement("SELECT serial,version,created_by_client,instance_id FROM schema_version ORDER BY serial DESC LIMIT 1");
  214.             if (stmt.executeStep()) {
  215.                 this.schemaVersion = stmt.getUTF8String(1);
  216.                 this.creatorClient = stmt.getUTF8String(2);
  217.                 this.instanceId = stmt.getUTF8String(3);
  218.             }
  219.         } catch (e) {
  220.             
  221.             this.schemaVersion = null;
  222.             this.creatorClient = null;
  223.             this.instanceId = null;
  224.         } finally {
  225.             if (stmt) {
  226.                 stmt.reset();
  227.                 try {
  228.                     stmt.finalize();
  229.                 } catch (ex) {
  230.                 }
  231.                 stmt = null;
  232.             }
  233.         }
  234.     },
  235.     
  236.     logRecsRequest: function(
  237.             req_time, 
  238.             resp_time, 
  239.             req_id,
  240.             cancelled) {
  241.         if (!this.available) {
  242.             return;
  243.         }
  244.         var conn = null;
  245.         var stmt = null;
  246.         try {
  247.             conn = this.connect();
  248.             if (!conn) { 
  249.                 return;
  250.             }
  251.             stmt = conn.createStatement("INSERT INTO rec_reqs (req_time,resp_time,req_id,cancelled,client_version) VALUES (?1,?2,?3,?4,?5)");
  252.             var reqt = req_time ? 
  253.                     Utils.toISO8601DateString(req_time) : null;
  254.             var rest = resp_time ? 
  255.                             Utils.toISO8601DateString(resp_time) : null;
  256.             stmt.bindUTF8StringParameter(0,reqt);
  257.             stmt.bindUTF8StringParameter(1,rest);
  258.             stmt.bindUTF8StringParameter(2,req_id);
  259.             stmt.bindInt32Parameter(3,cancelled ? 1 : 0);
  260.             stmt.bindUTF8StringParameter(4,CLIENT_INFO.version);
  261.             stmt.execute();
  262.         } catch (ex) {
  263.             
  264.             if (window.Components) {
  265.                 Components.utils.reportError(ex);
  266.             }
  267.         } finally {
  268.             if (stmt) {
  269.                 try {
  270.                     stmt.finalize();
  271.                 } catch (ex) {
  272.                 }
  273.                 stmt = null;
  274.             }
  275.             if (conn) {
  276.                 try {
  277.                     conn.close();
  278.                 } catch (ex) {
  279.                 }
  280.                 conn = null;
  281.             }
  282.         }
  283.     },
  284.     
  285.     logCachedRecsReqHit: function(
  286.             hit_time, 
  287.             req_id) {
  288.         if (!this.available) {
  289.             return;
  290.         }
  291.         var conn = null;
  292.         var stmt = null;
  293.         try {
  294.             conn = this.connect();
  295.             if (!conn) { 
  296.                 return;
  297.             }
  298.             stmt = conn.createStatement("INSERT INTO cached_rec_req_hits (hit_time,req_id,client_version) VALUES (?1,?2,?3)");
  299.             var hitt = Utils.toISO8601DateString(hit_time);
  300.             stmt.bindUTF8StringParameter(0,hitt);
  301.             stmt.bindUTF8StringParameter(1,req_id);
  302.             stmt.bindUTF8StringParameter(2,CLIENT_INFO.version);
  303.             stmt.execute();
  304.         } catch (ex) {
  305.             
  306.             if (window.Components) {
  307.                 Components.utils.reportError(ex);
  308.             }
  309.         } finally {
  310.             if (stmt) {
  311.                 try {
  312.                     stmt.finalize();
  313.                 } catch (ex) {
  314.                 }
  315.                 stmt = null;
  316.             }
  317.             if (conn) {
  318.                 try {
  319.                     conn.close();
  320.                 } catch (ex) {
  321.                 }
  322.                 conn = null;
  323.             }
  324.         }
  325.     },
  326.     
  327.     
  328.     logRecAction: function(
  329.             req_id, 
  330.             rec_id,
  331.             action,
  332.             destination,
  333.             real_estate_kind,
  334.             real_estate_position) {
  335.         if (!this.available) {
  336.             return;
  337.         }
  338.         var conn = null;
  339.         var stmt = null;
  340.         try {
  341.             conn = this.connect();
  342.             if (!conn) { 
  343.                 return;
  344.             }
  345.             stmt = conn.createStatement("INSERT INTO recs_actions (event_time,req_id,rec_id,client_version,action,destination,real_estate_kind,real_estate_position) VALUES (?1,?2,?3,?4,?5,?6,?7,?8)");
  346.             var evt = Utils.toISO8601DateString(new Date());
  347.             stmt.bindUTF8StringParameter(0,evt);
  348.             stmt.bindUTF8StringParameter(1,req_id);
  349.             stmt.bindUTF8StringParameter(2,rec_id);
  350.             stmt.bindUTF8StringParameter(3,CLIENT_INFO.version);
  351.             stmt.bindUTF8StringParameter(4,action);
  352.             stmt.bindUTF8StringParameter(5,destination);
  353.             stmt.bindUTF8StringParameter(6,real_estate_kind);
  354.             stmt.bindUTF8StringParameter(7,real_estate_position);
  355.             stmt.execute();
  356.         } catch (ex) {
  357.             
  358.             if (window.Components) {
  359.                 Components.utils.reportError(ex);
  360.             }
  361.         } finally {
  362.             if (stmt) {
  363.                 try {
  364.                     stmt.finalize();
  365.                 } catch (ex) {
  366.                 }
  367.                 stmt = null;
  368.             }
  369.             if (conn) {
  370.                 try {
  371.                     conn.close();
  372.                 } catch (ex) {
  373.                 }
  374.                 conn = null;
  375.             }
  376.         }
  377.     },
  378.     
  379.     logUserEvent: function(element,event,segments,value) {
  380.         if (!this.available) {
  381.             return;
  382.         }
  383.         var conn = null;
  384.         var stmt = null;
  385.         try {
  386.             conn = this.connect();
  387.             if (!conn) { 
  388.                 return;
  389.             }
  390.             stmt = conn.createStatement("INSERT INTO user_events (event_time,element,event,client_version,segments,value) VALUES (?1,?2,?3,?4,?5,?6)");
  391.             var evt = Utils.toISO8601DateString(new Date());
  392.             stmt.bindUTF8StringParameter(0,evt);
  393.             stmt.bindUTF8StringParameter(1,element);
  394.             stmt.bindUTF8StringParameter(2,event);
  395.             stmt.bindUTF8StringParameter(3,CLIENT_INFO.version);
  396.             if (segments) {
  397.                 stmt.bindUTF8StringParameter(4,Prototype.O.toJSON(segments));
  398.             }
  399.             stmt.bindUTF8StringParameter(5,value);
  400.             stmt.execute();
  401.         } catch (ex) {
  402.             
  403.             if (window.Components) {
  404.                 Components.utils.reportError(ex);
  405.             }
  406.         } finally {
  407.             if (stmt) {
  408.                 try {
  409.                     stmt.finalize();
  410.                 } catch (ex) {
  411.                 }
  412.                 stmt = null;
  413.             }
  414.             if (conn) {
  415.                 try {
  416.                     conn.close();
  417.                 } catch (ex) {
  418.                 }
  419.                 conn = null;
  420.             }
  421.         }
  422.     },
  423.     
  424.     prepareReportIfNecessary: function(report_send_interval_ms) {
  425.         if (!this.available) {
  426.             return false;
  427.         }
  428.         if (this.privateBrowsing.inPrivateBrowsing) {
  429.             // Disable reports in private browsing
  430.             return false;
  431.         }
  432.         var conn = null;
  433.         var stmt = null;
  434.         try {
  435.             conn = this.connect();
  436.             if (!conn) { 
  437.                 return;
  438.             }
  439.             // Check time of latest report
  440.             
  441.             var latest_report_time = null;
  442.             var last_rec_req_id = null;
  443.             var last_rec_action_id = null;
  444.             var last_cached_rec_req_hit_id = null;
  445.             var last_user_event_id = null;
  446.             try {
  447.                 // FIXME: Should we prevent multiple reporters from doing this in parallel,
  448.                 // in a stronger manner?
  449.                 stmt = conn.createStatement("SELECT report_time,last_rec_req_id,last_rec_action_id,last_cached_rec_req_hit_id,last_user_event_id FROM last_report");
  450.                 if (stmt.executeStep()) {
  451.                     latest_report_time = stmt.getInt64(0);
  452.                     if (stmt.getTypeOfIndex(1) !== stmt.VALUE_TYPE_NULL) {
  453.                         last_rec_req_id = stmt.getInt64(1);
  454.                     }
  455.                     if (stmt.getTypeOfIndex(2) !== stmt.VALUE_TYPE_NULL) {
  456.                         last_rec_action_id = stmt.getInt64(2);
  457.                     }
  458.                     if (stmt.getTypeOfIndex(3) !== stmt.VALUE_TYPE_NULL) {
  459.                         last_cached_rec_req_hit_id = stmt.getInt64(3);
  460.                     }
  461.                     if (stmt.getTypeOfIndex(4) !== stmt.VALUE_TYPE_NULL) {
  462.                         last_user_event_id = stmt.getInt64(4);
  463.                     }
  464.                 }
  465.             } finally {
  466.                 if (stmt) {
  467.                     stmt.reset();
  468.                     try {
  469.                         stmt.finalize();
  470.                     } catch (ex) {
  471.                     }
  472.                     stmt = null;
  473.                 }
  474.             }
  475.             var curTime = new Date();
  476.             //
  477.             //
  478.             if (latest_report_time === null) {
  479.                 
  480.                 this.createBaseReportRow(conn,curTime);
  481.                 return null;
  482.             }
  483.             //
  484.             if (report_send_interval_ms && ((curTime.getTime() - latest_report_time) < report_send_interval_ms)) {
  485.                 return null;
  486.             }
  487.             //
  488.             return this.prepareReport(conn,curTime,latest_report_time,last_rec_req_id,last_rec_action_id,last_cached_rec_req_hit_id,last_user_event_id);
  489.         } catch (ex) {
  490.             
  491.             if (window.Components) {
  492.                 Components.utils.reportError(ex);
  493.             }
  494.             return null;
  495.         } finally {
  496.             if (conn) {
  497.                 try {
  498.                     conn.close();
  499.                 } catch (ex) {
  500.                 }
  501.                 conn = null;
  502.             }
  503.         }
  504.     },
  505.     
  506.     prepareReport: function(conn,toTime,last_report_time,last_rec_req_id,last_rec_action_id,last_cached_rec_req_hit_id,last_user_event_id) {
  507.         var reportData = {
  508.                 to_time: toTime.getTime(),
  509.                 last_rec_req_id: last_rec_req_id,
  510.                 last_rec_action_id: last_rec_action_id,
  511.                 last_cached_rec_req_hit_id: last_cached_rec_req_hit_id,
  512.                 last_user_event_id: last_user_event_id,
  513.                 report: {
  514.                     ClientID: CLIENT_INFO.clientId,
  515.                     AppID: CLIENT_INFO.appId,
  516.                     ClientSchemaCreator: this.creatorClient,
  517.                     ClientSchemaInstanceID: this.instanceId,
  518.                     From: Utils.toISO8601DateString(new Date(last_report_time)),
  519.                     To: Utils.toISO8601DateString(toTime),
  520.                     Events: {
  521.                         RecommendationRequests: [],
  522.                         RecommendationActions: [],
  523.                         CachedRecommendationRequestHits: [],
  524.                         UserEvents: [],
  525.                     },
  526.                     Metrics: {
  527.                     }
  528.                 }
  529.         };
  530.     
  531.         var stmt = null;
  532.         try {
  533.             if (last_rec_req_id !== null) {
  534.                 stmt = conn.createStatement("SELECT id,req_time,resp_time,req_id,client_version,cancelled FROM rec_reqs WHERE id > ?1");
  535.                 stmt.bindInt64Parameter(0,last_rec_req_id);
  536.             } else {
  537.                 stmt = conn.createStatement("SELECT id,req_time,resp_time,req_id,client_version,cancelled FROM rec_reqs");
  538.             }
  539.             while (stmt.executeStep()) {
  540.                 var rec_req = {
  541.                         id: stmt.getInt64(0),
  542.                         req_time: stmt.getUTF8String(1),
  543.                         resp_time: stmt.getUTF8String(2),
  544.                         req_id: stmt.getUTF8String(3),
  545.                         client_version: stmt.getUTF8String(4),
  546.                         cancelled: (stmt.getInt32(5) !== 0)
  547.                 };
  548.                 reportData.report.Events.RecommendationRequests.push(rec_req);
  549.                 if ((reportData.last_rec_req_id === null) || (reportData.last_rec_req_id < rec_req.id)) {
  550.                     reportData.last_rec_req_id = rec_req.id;
  551.                 }
  552.             }
  553.         } finally {
  554.             if (stmt) {
  555.                 try {
  556.                     stmt.finalize();
  557.                 } catch (ex) {
  558.                 }
  559.                 stmt = null;
  560.             }
  561.         }
  562.     
  563.         try {
  564.             if (last_rec_action_id !== null) {
  565.                 stmt = conn.createStatement("SELECT id,event_time,req_id,rec_id,client_version,action,destination,real_estate_kind,real_estate_position FROM recs_actions WHERE id > ?1");
  566.                 stmt.bindInt64Parameter(0,last_rec_action_id);
  567.             } else {
  568.                 stmt = conn.createStatement("SELECT id,event_time,req_id,rec_id,client_version,action,destination,real_estate_kind,real_estate_position FROM recs_actions");
  569.             }
  570.             while (stmt.executeStep()) {
  571.                 var rec_action = {
  572.                         id: stmt.getInt64(0),
  573.                         event_time: stmt.getUTF8String(1),
  574.                         req_id: stmt.getUTF8String(2),
  575.                         rec_id: stmt.getUTF8String(3),
  576.                         client_version: stmt.getUTF8String(4),
  577.                         action: stmt.getUTF8String(5),
  578.                         destination: stmt.getUTF8String(6),
  579.                         real_estate_kind: stmt.getUTF8String(7),
  580.                         real_estate_position: stmt.getUTF8String(8)
  581.                 };
  582.                 reportData.report.Events.RecommendationActions.push(rec_action);
  583.                 if ((reportData.last_rec_action_id === null) || (reportData.last_rec_action_id < rec_action.id)) {
  584.                     reportData.last_rec_action_id = rec_action.id;
  585.                 }
  586.             }
  587.         } finally {
  588.             if (stmt) {
  589.                 try {
  590.                     stmt.finalize();
  591.                 } catch (ex) {
  592.                 }
  593.                 stmt = null;
  594.             }
  595.         }
  596.         
  597.         var stmt = null;
  598.         try {
  599.             if (last_cached_rec_req_hit_id !== null) {
  600.                 stmt = conn.createStatement("SELECT id,hit_time,req_id,client_version FROM cached_rec_req_hits WHERE id > ?1");
  601.                 stmt.bindInt64Parameter(0,last_cached_rec_req_hit_id);
  602.             } else {
  603.                 stmt = conn.createStatement("SELECT id,hit_time,req_id,client_version FROM cached_rec_req_hits");
  604.             }
  605.             while (stmt.executeStep()) {
  606.                 var cached_rec_req_hit = {
  607.                         id: stmt.getInt64(0),
  608.                         hit_time: stmt.getUTF8String(1),
  609.                         req_id: stmt.getUTF8String(2),
  610.                         client_version: stmt.getUTF8String(3)
  611.                 };
  612.                 reportData.report.Events.CachedRecommendationRequestHits.push(cached_rec_req_hit);
  613.                 if ((reportData.last_cached_rec_req_hit_id === null) || (reportData.last_cached_rec_req_hit_id < cached_rec_req_hit.id)) {
  614.                     reportData.last_cached_rec_req_hit_id = cached_rec_req_hit.id;
  615.                 }
  616.             }
  617.         } finally {
  618.             if (stmt) {
  619.                 try {
  620.                     stmt.finalize();
  621.                 } catch (ex) {
  622.                 }
  623.                 stmt = null;
  624.             }
  625.         }
  626.     
  627.         var stmt = null;
  628.         try {
  629.             if (last_user_event_id !== null) {
  630.                 stmt = conn.createStatement("SELECT id,event_time,element,event,client_version,segments,value FROM user_events WHERE id > ?1");
  631.                 stmt.bindInt64Parameter(0,last_user_event_id);
  632.             } else {
  633.                 stmt = conn.createStatement("SELECT id,event_time,element,event,client_version,segments,value FROM user_events");
  634.             }
  635.             while (stmt.executeStep()) {
  636.                 var user_event = {
  637.                         id: stmt.getInt64(0),
  638.                         event_time: stmt.getUTF8String(1),
  639.                         element: stmt.getUTF8String(2),
  640.                         event: stmt.getUTF8String(3),
  641.                         client_version: stmt.getUTF8String(4),
  642.                         segments: stmt.getUTF8String(5),
  643.                         value: stmt.getUTF8String(6),
  644.                 };
  645.                 if (user_event.segments) {
  646.                     user_event.segments = Prototype.S.decodeJSON(user_event.segments);
  647.                 }
  648.                 reportData.report.Events.UserEvents.push(user_event);
  649.                 if ((reportData.last_user_event_id === null) || (reportData.last_user_event_id < user_event.id)) {
  650.                     reportData.last_user_event_id = user_event.id;
  651.                 }
  652.             }
  653.         } finally {
  654.             if (stmt) {
  655.                 try {
  656.                     stmt.finalize();
  657.                 } catch (ex) {
  658.                 }
  659.                 stmt = null;
  660.             }
  661.         }
  662.  
  663.         return reportData;
  664.     },
  665.     
  666.     markReportAsSent: function(reportData) {
  667.         if (!this.available) {
  668.             return false;
  669.         }
  670.         var conn = null;
  671.         var stmt = null;
  672.         try {
  673.             conn = this.connect();
  674.             if (!conn) { 
  675.                 return;
  676.             }
  677.             // Insert the record into the database
  678.             try {
  679.                 stmt = conn.createStatement("UPDATE last_report SET report_time=?1, last_rec_req_id=?2, last_rec_action_id=?3, last_cached_rec_req_hit_id=?4, last_user_event_id=?5 WHERE id=1");
  680.                 stmt.bindInt64Parameter(0,reportData.to_time);
  681.                 if (reportData.last_rec_req_id === null) {
  682.                     stmt.bindNullParameter(1);
  683.                 } else {
  684.                     stmt.bindInt64Parameter(1,reportData.last_rec_req_id);
  685.                 }
  686.                 if (reportData.last_rec_action_id === null) {
  687.                     stmt.bindNullParameter(2);
  688.                 } else {
  689.                     stmt.bindInt64Parameter(2,reportData.last_rec_action_id);
  690.                 }
  691.                 if (reportData.last_cached_rec_req_hit_id === null) {
  692.                     stmt.bindNullParameter(3);
  693.                 } else {
  694.                     stmt.bindInt64Parameter(3,reportData.last_cached_rec_req_hit_id);
  695.                 }
  696.                 if (reportData.last_user_event_id === null) {
  697.                     stmt.bindNullParameter(4);
  698.                 } else {
  699.                     stmt.bindInt64Parameter(4,reportData.last_user_event_id);
  700.                 }
  701.                 stmt.execute();
  702.             } finally {
  703.                 if (stmt) {
  704.                     try {
  705.                         stmt.finalize();
  706.                     } catch (ex) {
  707.                     }
  708.                     stmt = null;
  709.                 }
  710.             }
  711.             // Try cleaning up unnecessary rows
  712.             if (reportData.last_rec_req_id !== null) {
  713.                 try {
  714.                     stmt = conn.createStatement("DELETE FROM rec_reqs WHERE id <= ?1");
  715.                     stmt.bindInt64Parameter(0,reportData.last_rec_req_id);
  716.                     stmt.execute();
  717.                 } finally {
  718.                     if (stmt) {
  719.                         try {
  720.                             stmt.finalize();
  721.                         } catch (ex) {
  722.                         }
  723.                         stmt = null;
  724.                     }
  725.                 }
  726.             }
  727.             if (reportData.last_rec_action_id !== null) {
  728.                 try {
  729.                     stmt = conn.createStatement("DELETE FROM recs_actions WHERE id <= ?1");
  730.                     stmt.bindInt64Parameter(0,reportData.last_rec_action_id);
  731.                     stmt.execute();
  732.                 } finally {
  733.                     if (stmt) {
  734.                         try {
  735.                             stmt.finalize();
  736.                         } catch (ex) {
  737.                         }
  738.                         stmt = null;
  739.                     }
  740.                 }
  741.             }
  742.             if (reportData.last_cached_rec_req_hit_id !== null) {
  743.                 try {
  744.                     stmt = conn.createStatement("DELETE FROM cached_rec_req_hits WHERE id <= ?1");
  745.                     stmt.bindInt64Parameter(0,reportData.last_cached_rec_req_hit_id);
  746.                     stmt.execute();
  747.                 } finally {
  748.                     if (stmt) {
  749.                         try {
  750.                             stmt.finalize();
  751.                         } catch (ex) {
  752.                         }
  753.                         stmt = null;
  754.                     }
  755.                 }
  756.             }
  757.             if (reportData.last_user_event_id !== null) {
  758.                 try {
  759.                     stmt = conn.createStatement("DELETE FROM user_events WHERE id <= ?1");
  760.                     stmt.bindInt64Parameter(0,reportData.last_user_event_id);
  761.                     stmt.execute();
  762.                 } finally {
  763.                     if (stmt) {
  764.                         try {
  765.                             stmt.finalize();
  766.                         } catch (ex) {
  767.                         }
  768.                         stmt = null;
  769.                     }
  770.                 }
  771.             }
  772.         } catch (ex) {
  773.             
  774.             if (window.Components) {
  775.                 Components.utils.reportError(ex);
  776.             }
  777.             return null;
  778.         } finally {
  779.             if (stmt) {
  780.                 try {
  781.                     stmt.finalize();
  782.                 } catch (ex) {
  783.                 }
  784.                 stmt = null;
  785.             }
  786.             if (conn) {
  787.                 try {
  788.                     conn.close();
  789.                 } catch (ex) {
  790.                 }
  791.                 conn = null;
  792.             }
  793.         }
  794.     },
  795.  
  796.     getRecLastViewedInTeaserTime: function(url) {
  797.         if (!this.available) {
  798.             return null;
  799.         }
  800.         var conn = null;
  801.         var stmt = null;
  802.         try {
  803.             conn = this.connect();
  804.             if (!conn) { 
  805.                 return;
  806.             }
  807.             stmt = conn.createStatement("SELECT last_viewed_in_teaser_time FROM recs_acks WHERE url = ?1");
  808.             stmt.bindUTF8StringParameter(0,url);
  809.             while (stmt.executeStep()) {
  810.                 return new Date(stmt.getInt64(0));
  811.             }
  812.             return null;
  813.         } catch (ex) {
  814.             
  815.             if (window.Components) {
  816.                 Components.utils.reportError(ex);
  817.             }
  818.         } finally {
  819.             if (stmt) {
  820.                 try {
  821.                     stmt.finalize();
  822.                 } catch (ex) {
  823.                 }
  824.                 stmt = null;
  825.             }
  826.             if (conn) {
  827.                 try {
  828.                     conn.close();
  829.                 } catch (ex) {
  830.                 }
  831.                 conn = null;
  832.             }
  833.         }
  834.     },
  835.     
  836.     markAckedRecAsReceived: function(url,received_time) {
  837.         if (!this.available) {
  838.             return;
  839.         }
  840.         var conn = null;
  841.         var stmt = null;
  842.         try {
  843.             conn = this.connect();
  844.             if (!conn) { 
  845.                 return;
  846.             }
  847.             stmt = conn.createStatement("UPDATE recs_acks SET last_rec_received_time = ?1 WHERE url = ?2");
  848.             stmt.bindInt64Parameter(0,received_time.getTime());
  849.             stmt.bindUTF8StringParameter(1,url);
  850.             stmt.execute();
  851.         } catch (ex) {
  852.             
  853.             if (window.Components) {
  854.                 Components.utils.reportError(ex);
  855.             }
  856.         } finally {
  857.             if (stmt) {
  858.                 try {
  859.                     stmt.finalize();
  860.                 } catch (ex) {
  861.                 }
  862.                 stmt = null;
  863.             }
  864.             if (conn) {
  865.                 try {
  866.                     conn.close();
  867.                 } catch (ex) {
  868.                 }
  869.                 conn = null;
  870.             }
  871.         }
  872.     },
  873.     
  874.     markRecAsViewedInTeaser: function(url,received_time) {
  875.         if (!this.available) {
  876.             return;
  877.         }
  878.         var conn = null;
  879.         var stmt = null;
  880.         try {
  881.             conn = this.connect();
  882.             if (!conn) { 
  883.                 return;
  884.             }
  885.             var now = new Date().getTime();
  886.             try {
  887.                 stmt = conn.createStatement("INSERT OR REPLACE INTO recs_acks (url,last_viewed_in_teaser_time,last_rec_received_time) VALUES (?1,?2,?3)");
  888.                 stmt.bindUTF8StringParameter(0,url);
  889.                 stmt.bindInt64Parameter(1,now);
  890.                 stmt.bindInt64Parameter(2,received_time.getTime());
  891.                 stmt.execute();
  892.             } finally {
  893.                 if (stmt) {
  894.                     try {
  895.                         stmt.finalize();
  896.                     } catch (ex) {
  897.                     }
  898.                     stmt = null;
  899.                 }
  900.             }
  901.             try {
  902.                 var cleanupMarkerTime = now - Prefs.db_recs_acks_max_age_millis;
  903.                 stmt = conn.createStatement("DELETE FROM recs_acks WHERE last_viewed_in_teaser_time < ?1 AND last_rec_received_time < ?2");
  904.                 stmt.bindInt64Parameter(0,cleanupMarkerTime);
  905.                 stmt.bindInt64Parameter(1,cleanupMarkerTime);
  906.                 stmt.execute();
  907.             } finally {
  908.                 if (stmt) {
  909.                     try {
  910.                         stmt.finalize();
  911.                     } catch (ex) {
  912.                     }
  913.                     stmt = null;
  914.                 }
  915.             }
  916.         } catch (ex) {
  917.             
  918.             if (window.Components) {
  919.                 Components.utils.reportError(ex);
  920.             }
  921.         } finally {
  922.             if (conn) {
  923.                 try {
  924.                     conn.close();
  925.                 } catch (ex) {
  926.                 }
  927.                 conn = null;
  928.             }
  929.         }
  930.     },
  931.  
  932.     upgradeToV0_1: function(conn,version) {
  933.         if (version != "0.1") {
  934.             throw "Unsupported version number for upgrade: " + version;
  935.         }
  936.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS schema_version (serial INTEGER PRIMARY KEY ASC AUTOINCREMENT, version VARCHAR(30), created_by_client VARCHAR(255), instance_id VARCHAR(36))");
  937.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS rec_reqs (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, req_time VARCHAR(23), resp_time VARCHAR(23), req_id VARCHAR(255), client_version VARCHAR(255), cancelled INTEGER NOT NULL)");
  938.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS recs_followed (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, event_time VARCHAR(23), req_id VARCHAR(255) NOT NULL, rec_id VARCHAR(255) NOT NULL, client_version VARCHAR(255))");
  939.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS reports (id INTEGER PRIMARY KEY, report_time INTEGER NOT NULL, last_rec_req_id INTEGER NULL, last_rec_followed_id INTEGER NULL)");
  940.     },
  941.  
  942.     upgradeToV0_2: function(conn,version) {
  943.         if (version != "0.2") {
  944.             throw "Unsupported version number for upgrade: " + version;
  945.         }
  946.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS recs_acks (url VARCHAR(2047) PRIMARY KEY, last_viewed_in_teaser_time INTEGER, last_rec_received_time INTEGER NOT NULL)");
  947.         conn.executeSimpleSQL("CREATE INDEX IF NOT EXISTS recs_acks_last_viewed_in_teaser_time_idx ON recs_acks (last_viewed_in_teaser_time ASC)");
  948.         conn.executeSimpleSQL("CREATE INDEX IF NOT EXISTS recs_acks_last_rec_received_time_idx ON recs_acks (last_rec_received_time ASC)");
  949.     },
  950.     
  951.     upgradeToV0_3: function(conn,version) {
  952.         if (version != "0.3") {
  953.             throw "Unsupported version number for upgrade: " + version;
  954.         }
  955.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS cached_rec_req_hits (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, hit_time VARCHAR(23) NOT NULL, req_id VARCHAR(255), client_version VARCHAR(255))");
  956.         conn.executeSimpleSQL("ALTER TABLE reports ADD COLUMN last_cached_rec_req_hit_id INTEGER NULL");
  957.     },
  958.  
  959.     upgradeToV0_4: function(conn,version) {
  960.         if (version != "0.4") {
  961.             throw "Unsupported version number for upgrade: " + version;
  962.         }
  963.         // Add recommendation actions and real estate information
  964.         conn.executeSimpleSQL("ALTER TABLE recs_followed RENAME TO recs_actions");
  965.         // action is one of OPEN or SHARE for now
  966.         conn.executeSimpleSQL("ALTER TABLE recs_actions ADD COLUMN action VARCHAR(40) NOT NULL DEFAULT 'OPEN'");
  967.         // destination is:
  968.         // * CURRENT_TAB, NEW_TAB, NEW_WINDOW, FOCUS_EXISTING, or MAIL for the OPEN action
  969.         // * one the sharing destinations for the SHARE action 
  970.         conn.executeSimpleSQL("ALTER TABLE recs_actions ADD COLUMN destination VARCHAR(40) NULL DEFAULT NULL");
  971.         // real_estate_kind is one of STATUSBAR_TICKER, TEASER, or STATUSBAR_POPUP
  972.         conn.executeSimpleSQL("ALTER TABLE recs_actions ADD COLUMN real_estate_kind VARCHAR(40) NULL DEFAULT NULL");
  973.         // real_estate_position is a description, specific to the real_estate_kind, of exactly where the recommendation was located inside the real estate
  974.         conn.executeSimpleSQL("ALTER TABLE recs_actions ADD COLUMN real_estate_position VARCHAR(255) NULL DEFAULT NULL");
  975.  
  976.         // Add other user events table
  977.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS user_events (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, event_time VARCHAR(23), element VARCHAR(255) NOT NULL, event VARCHAR(255) NOT NULL, client_version VARCHAR(255), segments VARCHAR(255) DEFAULT NULL, value VARCHAR(255) DEFAULT NULL)");
  978.         
  979.         // We create a new last_report table to replace the reports table. We also add a new column and rename the deprecated last_rec_followed_id
  980.         
  981.         conn.executeSimpleSQL("ALTER TABLE reports RENAME TO reports_old");
  982.         conn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS last_report (id INTEGER PRIMARY KEY, report_time INTEGER NOT NULL, last_rec_req_id INTEGER NULL, last_rec_action_id INTEGER NULL, last_cached_rec_req_hit_id INTEGER NULL, last_user_event_id INTEGER NULL)");
  983.         conn.executeSimpleSQL("INSERT INTO last_report (id,report_time, last_rec_req_id, last_rec_action_id, last_cached_rec_req_hit_id) SELECT 1,report_time,last_rec_req_id,last_rec_followed_id,last_cached_rec_req_hit_id FROM reports_old ORDER BY id DESC LIMIT 1");
  984.         conn.executeSimpleSQL("DROP TABLE reports_old");
  985.     }
  986.  
  987. });
  988.  
  989.  
  990.  
  991. var LOGGING_DB = new LoggingDB();
  992.  
  993.